Skip to content

fix(mesh-store): replay pending deliveries to an agent returning from downtime - #30

Merged
Mearman merged 1 commit into
mainfrom
feat/downtime-delivery-replay
Sep 9, 2026
Merged

fix(mesh-store): replay pending deliveries to an agent returning from downtime#30
Mearman merged 1 commit into
mainfrom
feat/downtime-delivery-replay

Conversation

@Mearman

@Mearman Mearman commented Sep 9, 2026

Copy link
Copy Markdown
Member

Fixes #28 — the design decision recorded on the issue: implement replay, because for push bridges the missing wake-up is not noise, it is the "pi never wakes up" failure class from #14 wearing a different hat.

Delivery queues were per-store maps excluded from serialised state, so a restarted bridge found downtime messages in synced history but was never woken for them. Every peer already replicates delivery events in its own queues, so the queues now travel in SerialisedState, and applying a snapshot fires onDelivery for events targeting the receiving peer's own agent — a returning process replays exactly what accumulated while it was down. Drain bridges get the same benefit through their own queue after the merge.

Replay dedup is two-layered:

  • room messages and DMs the agent has already read are skipped — read receipts mutate the event between snapshots, so a structural key alone would miss and re-fire (this was a real bug caught by the tests);
  • a bounded structural key set covers the remaining event types.

An event fired locally is dropped from the local pending queue, so steady-state snapshots stop carrying what a peer already consumed; peers that never fired keep their copies — that is what a restart replays from. Queues are bounded per agent (oldest dropped first), and the stale-agent cleanup now purges queue entries with their agent, fixing a pre-existing unbounded leak.

Tests: unit replay + dedup + queue bound (wire-accurate snapshots), plus the end-to-end acceptance — a real TLS restart over a persisted identity receives the push for a message sent while it was down. Full suite 20 tests, three consecutive clean rounds, lint/typecheck/build green.

Wire note: SerialisedState gains deliveryQueues, so a mesh needs all peers on builds that understand it, same single-build contract as the entity revisions in #29.

… downtime

Delivery queues were per-store in-memory maps excluded from serialised
state, so a restarted bridge found downtime messages in synced history
but was never woken for them: no push, no drain, until something new
arrived. Every peer already replicates delivery events in its own
queues, so the queues now travel in state snapshots, and applying a
snapshot fires onDelivery for events targeting the receiving peer's own
agent: a returning process replays exactly what accumulated while it
was down.

Replay is deduplicated two ways: room messages and DMs this agent has
already read are skipped (read receipts mutate the event between
snapshots, so a structural key alone would miss and re-fire), and a
bounded structural key set covers the rest. An event fired locally is
also dropped from the local pending queue, so steady-state snapshots
stop carrying what a peer has already consumed; peers that never fired
the event keep their copies, which is what a restart replays from.

Queues are bounded per target agent (oldest dropped first) and are
purged with their agent by the stale cleanup, which previously leaked
queue entries for purged agents indefinitely.

The wire format's SerialisedState gains the deliveryQueues field, so a
mesh must be on a single build, as with the entity revisions.
@Mearman
Mearman marked this pull request as ready for review September 9, 2026 10:03
@Mearman
Mearman merged commit 39a2020 into main Sep 9, 2026
5 checks passed
@Mearman
Mearman deleted the feat/downtime-delivery-replay branch September 9, 2026 10:03
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 9, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
🔒 Security Review Completed 2026-09-09T10:11:22.802740Z 84bf0f7 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 1.25.6 🎉

The release is available on:

Your semantic-release bot 📦🚀

Mearman added a commit that referenced this pull request Sep 10, 2026
A legacy peer's state_sync payload can genuinely be missing deliveryQueues
(#30) or an entity's version field (#29), since nothing validates a
state_sync message's shape beyond isMeshMessage's bare method check.
normaliseWireState fills in the defaults (empty collections, version 1)
before the payload reaches applyStateSync, so an old-build snapshot parses
to a complete state instead of throwing.

WireStateInput models exactly that tolerance as its own type rather than
widening SerialisedState itself, so domain code elsewhere keeps working with
the fully-populated type.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Delivery events queued while an agent's process was down are not push-delivered on its return

1 participant